package limiter;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.*;

/**
 * @author dushougudu
 */
public class SlidingWindowLimiter {
    /**
     * 单位时间划分的小周期（单位时间是1分钟，10s一个小格子窗口，一共6个格子）
     */
    private int SUB_CYCLE = 10;

    /**
     * 每分钟限流请求数
     */
    private int thresholdPerMin = 100;

    /**
     * 计数器, k-为当前窗口的开始时间值秒，value为当前窗口的计数
     */
    private final TreeMap<Long, Integer> counters = new TreeMap<>();

    public SlidingWindowLimiter(int threshold){

    }

    /**
     * 滑动窗口时间算法实现
     */
    public boolean slidingWindowsTryAcquire() {
        //获取当前时间在哪个小周期窗口
        long currentWindowTime = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) / SUB_CYCLE * SUB_CYCLE;
        //当前窗口总请求数
        int currentWindowNum = countCurrentWindow(currentWindowTime);

        //超过阀值限流
        if (currentWindowNum >= thresholdPerMin) {
            return false;
        }

        //计数器+1
        int count = counters.get(currentWindowTime);
        counters.put(currentWindowTime, ++count);
        return true;
    }

    /**
     * 统计当前窗口的请求数
     */
    private int countCurrentWindow(long currentWindowTime) {
        //计算窗口开始位置
        long startTime = currentWindowTime - SUB_CYCLE * (60 / SUB_CYCLE - 1);
        int count = 0;

        //遍历存储的计数器
        Iterator<Map.Entry<Long, Integer>> iterator = counters.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Long, Integer> entry = iterator.next();
            // 删除无效过期的子窗口计数器
            if (entry.getKey() < startTime) {
                iterator.remove();
            } else {
                //累加当前窗口的所有计数器之和
                count = count + entry.getValue();
            }
        }
        return count;
    }



    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FixedWindowLimiter fixedWindowLimiter = new FixedWindowLimiter(1000, 5);

        CyclicBarrier barrier = new CyclicBarrier(5);
        CompletableFuture<Boolean>[] array = new CompletableFuture[5];
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>());
        for (int i = 0; i < 5; i++) {
            CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
                try {
                    barrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                boolean flag = fixedWindowLimiter.fixedWindowsTryAcquire();
                return flag;
            }, executor);
            array[i] = future;
        }

        for (CompletableFuture<Boolean> future : array) {
            System.out.println(future.get());
        }

    }

}
